"""
@author: 江同学呀
@file:  bulk_api.py
@date: 2025/3/19 22:53
@desc: 
    https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-bulk.html
"""
from typing import Union, Iterable, Literal, Dict, Type, List, Optional, Tuple, Any

from elastic_transport import ObjectApiResponse
from elasticsearch import helpers

from espc.common.common import Number
from espc.core.core import core
from espc.orm.api.base_api import BaseApi
from espc.orm.model.model.base_document import BaseDocument
from espc.orm.model.model.base_index import BaseIndex


class BulkApi(BaseApi):
    """
    批量
    todo
    """

    def __init__(self):
        super().__init__()
        self._body: Dict[str, Union[str, List, Dict, Number]] = {}

        self._docs: List[Union[BaseDocument, Dict]] = []
        return

    def docs(
            self, docs: Union[BaseDocument, Dict, Iterable[Union[BaseDocument, Dict]]],
            index: Union[str, Type[BaseIndex]] = None,
            op_type: Literal["create", "index", "update", "delete"] = "index"
    ) -> "BulkApi":
        """
        批量文档
        :param docs:
        :param index:
        :param op_type:
        :return:
        """
        if isinstance(docs, BaseDocument):
            self._docs.append({
                "_op_type": op_type,
                "_index": index.index_name if isinstance(index, BaseIndex) and issubclass(index, BaseIndex) else index,
                "_source": docs.serialize()
            })
        elif isinstance(docs, Dict):
            self._docs.append(docs)
        else:
            self._docs.extend(
                {
                    "_op_type": op_type,
                    "_index": index.index_name if isinstance(index, Type) and issubclass(index, BaseIndex) else index,
                    "_source": d.serialize() if isinstance(d, BaseDocument) else d
                }
                for d in docs
            )
        return self

    def post(
            self, human: bool = None, filter_path: Union[str, Iterable[str]] = None,
            # flat_settings: bool = None,
            error_trace: bool = None, pipeline: str = None, refresh: Literal["true", "false", "wait_for"] = None,
            require_alias: bool = None, routing: str = None, source: Union[bool, str, Iterable[str]] = None,
            source_excludes: Union[str, Iterable[str]] = None, source_includes: Union[str, Iterable[str]] = None,
            timeout: str = None, wait_for_active_shards: str = None, *args, **kwargs
    ) -> Dict:
        """
        POST请求
        :param human:
            Statistics are returned in a format suitable for humans (e.g. "exists_time": "1h" or "size": "1kb") and for
            computers (e.g. "exists_time_in_millis": 3600000 or "size_in_bytes": 1024). The human readable values can be
            turned off by adding ?human=false to the query string. This makes sense when the stats results are being
            consumed by a monitoring tool, rather than intended for human consumption. The default for the human flag is
            false.
            统计数据以适合人类的格式返回（例如“exists_time”： “1h”或“size”： “1kb”）和计算机（例如“exists_time_in_millis”： 3600000
            或 “size_in_bytes”： 1024）。可以通过将 ？human=false 添加到查询字符串来关闭人类可读值。当统计结果由监控工具使用而不是供人类
            使用时，这是有意义的。人为标志的默认值为 false。
        :param filter_path:
            All REST APIs accept a filter_path parameter that can be used to reduce the response returned by
            Elasticsearch. This parameter takes a comma separated list of filters expressed with the dot notation.
            所有 REST API 都接受 filter_path 参数，该参数可用于减少 Elasticsearch 返回的响应。此参数采用逗号分隔的过滤器列表，以点表示
            法表示
        # :param flat_settings:
        #     The flat_settings flag affects rendering of the lists of settings. When the flat_settings flag is true,
        #     settings are returned in a flat format.
        #     flat_settings 标志会影响设置列表的呈现。当 flat_settings 标志为 true 时，设置以平面格式返回
        :param error_trace:
            By default when a request returns an error Elasticsearch doesn’t include the stack trace of the error. You
            can enable that behavior by setting the error_trace url parameter to true.
            默认情况下，当请求返回错误时，Elasticsearch 不包含错误的堆栈跟踪。您可以通过将 error_trace url 参数设置为 true 来启用该行为。
        :param pipeline:
            (Optional, string) ID of the pipeline to use to preprocess incoming documents.
            （可选，字符串）用于预处理传入文档的管道的 ID。
        :param refresh:
            (Optional, enum) If true, Elasticsearch refreshes the affected shards to make this operation visible to
            search, if wait_for then wait for a refresh to make this operation visible to search, if false do nothing
            with refreshes. Valid values: true, false, wait_for. Default: false.
            （可选，枚举）如果为 true，则 Elasticsearch 刷新受影响的分片以使此作对搜索可见，如果wait_for为 true，则等待刷新以使此作对
            搜索可见，如果为 false，则不对刷新执行任何作。有效值：true、false wait_for。默认值：false。
        :param require_alias:
            (Optional, Boolean) If true, the request’s actions must target an index alias. Defaults to false.
            （可选，布尔值）如果为 true，则请求的作必须以索引别名为目标。默认为 false。
        :param routing:
            (Optional, string) Custom value used to route operations to a specific shard.
            （可选，字符串）用于将作路由到特定分片的自定义值。
        :param source:
            (Optional, string) True or false to return the _source field or not, or a list of fields to return.
            （可选，字符串）True 或 false 以返回或不返回_source字段，或要返回的字段列表。
        :param source_excludes:
            (Optional, string) A comma-separated list of source fields to exclude from the response.
            You can also use this parameter to exclude fields from the subset specified in _source_includes query parameter.
            If the _source parameter is false, this parameter is ignored.

            （可选，字符串）要从响应中排除的源字段的逗号分隔列表。
            您还可以使用此参数从 query 参数中指定的子集_source_includes排除字段。
            如果 _source 参数为 false，则忽略该参数。
        :param source_includes:
            (Optional, string) A comma-separated list of source fields to include in the response.
            If this parameter is specified, only these source fields are returned. You can exclude fields from this
            subset using the _source_excludes query parameter.
            If the _source parameter is false, this parameter is ignored.

            （可选，字符串）要包含在响应中的源字段的逗号分隔列表。
            如果指定了此参数，则仅返回这些源字段。您可以使用 _source_excludes 查询参数从此子集中排除字段。
            如果 _source 参数为 false，则忽略该参数。
        :param timeout:
            (Optional, time units) Period each action waits for the following operations:
                Automatic index creation
                Dynamic mapping updates
                Waiting for active shards
            Defaults to 1m (one minute). This guarantees Elasticsearch waits for at least the timeout before failing.
            The actual wait time could be longer, particularly when multiple waits occur.

            （可选，时间单位）周期每个作等待以下作：
                自动创建索引
                动态映射更新
                等待活动分片
            默认为 1m（1 分钟）。这保证了 Elasticsearch 在失败之前至少等待超时。实际等待时间可能会更长，尤其是在发生多个等待时。
        :param wait_for_active_shards:
            (Optional, string) The number of shard copies that must be active before proceeding with the operation. Set
            to all or any positive integer up to the total number of shards in the index (number_of_replicas+1).
            Default: 1, the primary shard.
            （可选，字符串）在继续执行作之前必须处于活动状态的分片副本数。设置为全部或任何正整数，最大为索引中的分片总数
            （number_of_replicas+1）。默认值：1，主分片。
        :param args:
        :param kwargs:
        :return:
        """
        success, failed = helpers.bulk(
            core.es,
            actions=self._docs,

            human=human,
            filter_path=filter_path,
            # flat_settings=flat_settings,
            error_trace=error_trace,
            pipeline=pipeline,
            refresh=refresh,
            require_alias=require_alias,
            routing=routing,
            source=source,
            source_excludes=source_excludes,
            source_includes=source_includes,
            timeout=timeout,
            wait_for_active_shards=wait_for_active_shards,
            *args, **kwargs
        )
        return {"success": success, "failed": failed}

